﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Statements;
using Weazel.Badger.Vhdl.Operators;
using Weazel.Badger.Vhdl.Operators.Unary;
using Weazel.Badger.Vhdl.Operators.Binary;

#endregion

namespace Weazel.Badger
{
  public class ExpressionConverter
  {
		/// <summary>
		/// 
		/// </summary>
    private ConversionEntity context;

		/// <summary>
		/// 
		/// </summary>
    private Gezel.Model.SignalFlowGraph signalFlowGraph;

    private List<Vhdl.Signal> signalsCreated = new List<Weazel.Badger.Vhdl.Signal>();
    private List<Vhdl.Signal> signalsWritten = new List<Weazel.Badger.Vhdl.Signal>();
    private List<Vhdl.Signal> signalsRead = new List<Weazel.Badger.Vhdl.Signal>();

    private List<Vhdl.ConstantDeclaration> constants = new List<Weazel.Badger.Vhdl.ConstantDeclaration>();

    private StatementCollection statements = new StatementCollection();

    private StatementCollection displayStatements = new StatementCollection();
    public StatementCollection DisplayStatements
    {
      get
      {
        return displayStatements;
      }

      set
      {
        displayStatements = value;
      }
    } 


    #region Properties
    public List<Vhdl.Signal> SignalsCreated
    {
      get
      {
        return signalsCreated;
      }      
    }
    
    public List<Vhdl.Signal> SignalsWritten
    {
      get
      {
        return signalsWritten;
      }
    }
    
    public List<Vhdl.Signal> SignalsRead
    {
      get
      {
        return signalsRead;
      }
    }

    public List<Vhdl.ConstantDeclaration> Constants
    {
      get
      {
        return constants;
      }
    }

    public StatementCollection Statements
    {
      get
      {
        return statements;
      }
    }
    #endregion

		/// <summary>
		/// This method is used to insert a signal assignment expression. The method takes
		/// as an argument an expression. A signal of type std_logic or std_logic_vector is
		/// created and assigned by the value of the argument expression. The method returns
		/// a reference expression to this signal. An assignment statement is automatically 
		/// inserted into the current statement collection
		/// </summary>
		/// <param name="statements">The "current" statement collection</param>
		/// <param name="expr">expression to wrap</param>
		/// <param name="gezelType">type of expression</param>
		/// <returns>Signal reference expression referencing</returns>
		private Vhdl.Expressions.SignalReferenceExpression insertSignalAssignmentExpression(
			StatementCollection statements,
			Vhdl.Expressions.Expression expr,
			Vhdl.GezelType gezelType
			)
		{
			// create new signal

			Weazel.Badger.Vhdl.Types.Type t = gezelType.Width > 1 ?
					(Weazel.Badger.Vhdl.Types.Type)new StdLogicVector(gezelType.Width)
				: (Weazel.Badger.Vhdl.Types.Type)new StdLogic();

			Vhdl.Signal signal = new Weazel.Badger.Vhdl.Signal(t, gezelType);

			// add new signal assign. statement ontop of current position
			statements.Add(new SignalAssignmentStatement(signal, expr));

			// add to entity
			signalsCreated.Add(signal);
			signalsWritten.Add(signal);

			// we assume this signal to be read at a later time
			signalsRead.Add(signal);

			return new Vhdl.Expressions.SignalReferenceExpression(statements, signal);
		}

    private Vhdl.Expressions.SignalReferenceExpression insertSignalAssignmentExpression(
      StatementCollection statements,
      Vhdl.Expressions.Expression expr)
    {
      // create new signal
      Weazel.Badger.Vhdl.Types.Type t = expr.GezelType.Width > 1 ?
          (Weazel.Badger.Vhdl.Types.Type)new StdLogicVector(expr.GezelType.Width)
        : (Weazel.Badger.Vhdl.Types.Type) new StdLogic();

      Vhdl.Signal signal = new Weazel.Badger.Vhdl.Signal(t, expr.GezelType);

      // add new signal assign. statement ontop of current position
      statements.Add(new SignalAssignmentStatement(signal, expr));

      // add to entity
      signalsCreated.Add(signal);
      signalsWritten.Add(signal);

      // we assume this signal to be read at a later time
      signalsRead.Add(signal);

      return new Vhdl.Expressions.SignalReferenceExpression(statements, signal);
    }

		/// <summary>
		/// Convert a Gezel ternary expression (the ?: operator)
		/// </summary>
		/// <param name="statements">The "current" statement collection</param>
		/// <param name="signal">Signal being assigned by the ternary operator</param>
		/// <param name="ternaryExpr"></param>
		/// <param name="expandExpression"></param>
		/// <returns>If-statement representing the ternary oprerator in VHDL</returns>
    private IfStatement convertTernaryExpression(
      StatementCollection statements, 
      Vhdl.Signal signal,
      Gezel.Model.TernaryExpression ternaryExpr,
      bool expandExpression)
    {
      if (signal == null)
        throw new NullReferenceException("convertTernaryExpression(): Signal cannot be null.");

      Gezel.Model.TernaryExpression te = ternaryExpr;

      IfStatement ifStatement = new IfStatement();

      Vhdl.Expressions.Expression cond = 
        ConvertExpression(
          statements, 
          te.Condition,
          expandExpression
        );

        Vhdl.Expressions.BinaryExpression g = 
          new Vhdl.Expressions.BinaryExpression(
						statements,
            BinaryOperators.Equals,
            cond,
            new Vhdl.Expressions.ConstantExpression(
							statements, 
							"'1'", 
							new StdLogic(), 
							null
					  ),
            new Vhdl.Types.Boolean(),
            null
          );

        ifStatement.Condition = g;

      Vhdl.Expressions.Expression trueBranch =
        ConvertExpression(
          statements,
          te.LeftChild,
          expandExpression,
          signal.Type.Width,
          ternaryExpr.Signed
        );

      ifStatement.TrueStatements.Add(
        new SignalAssignmentStatement(
          signal, 
          trueBranch
        )       
      );

      Vhdl.Expressions.Expression falseBranch =
        ConvertExpression(
          statements,
          te.RightChild,
          expandExpression,
          signal.Type.Width,
          ternaryExpr.Signed
        );

      ifStatement.FalseStatements.Add(
        new SignalAssignmentStatement(
          signal,
          falseBranch
        )
      );



      //// pointer to last 'ifstatement' in chain
      //IfStatement lastElsIfStatement = ifStatement;

      //while (te.RightChild is Gezel.Model.TernaryExpression)
      //{
      //  te = (Gezel.Model.TernaryExpression)te.RightChild;

      //  IfStatement elsIfStatement = new IfStatement();

      //  trueBranch =
      //    ConvertExpression(
      //      elsIfStatement.TrueStatements,
      //      te.LeftChild,
      //      expandExpression,
      //      signal.Type.Width,
      //      ternaryExpr.Signed
      //    );

      //  elsIfStatement.Condition = 
      //    ConvertExpression(
      //      statements, 
      //      te.Condition,            
      //      expandExpression,
      //      signal.Type.Width,
      //      ternaryExpr.Signed
      //    );

      //  elsIfStatement.TrueStatements.Add(
      //    new SignalAssignmentStatement(
      //      signal, 
      //      trueBranch
      //    )
      //  );

      //  // update pointer
      //  lastElsIfStatement.ElsIfStatement = elsIfStatement;
      //  lastElsIfStatement = elsIfStatement;
      //}

      //ifStatement.FalseStatements.Add(
      //  new SignalAssignmentStatement(
      //    signal, 
      //    ConvertExpression(
      //      ifStatement.FalseStatements, 
      //      te.RightChild,
      //      expandExpression,
      //      signal.Type.Width,
      //      ternaryExpr.Signed
      //    )
      //  )
      //);

      return ifStatement;

    }

    public void ConvertAssignmentExpression(Gezel.Model.BinaryExpression expr, bool expandExpressions)
    {
      ConvertAssignmentExpression(this.statements, expr, expandExpressions);
    }

    public void ConvertAssignmentExpression(
      StatementCollection statements, 
      Gezel.Model.BinaryExpression binExpr, 
      bool expandExpression
    )
    {
      // Left child must be name of signal or (out)port
      System.Diagnostics.Debug.Assert(binExpr.LeftChild is Gezel.Model.LeafExpression);

      string signal = ((Gezel.Model.LeafExpression)binExpr.LeftChild).Value;

      Vhdl.Signal assignee = context.FindSignal(signal);

      if (assignee == null)  // perhaps a local signal?
        assignee = context.FindSignal(signalFlowGraph.LocalName + "_" + signal);

      if (assignee == null)
      {
        string message =
          string.Format(
            "Failed to locate signal '{0}' during conversion of assignment expression",
            signal
          );

        throw new Vhdl.VhdlModelException(message);
      }

      if (context.IsRegister(assignee))
      {
        assignee = context.FindSignal(signal + "_wire");
      }

      if (!signalsWritten.Contains(assignee))
        signalsWritten.Add(assignee);

      // special care must be taken with ternary and nested ternary expressions
      if (binExpr.RightChild is Gezel.Model.TernaryExpression)
      {
        Gezel.Model.TernaryExpression te = (Gezel.Model.TernaryExpression)binExpr.RightChild;

        statements.Add(convertTernaryExpression(statements, assignee, te, expandExpression));
      }
      else
      {
				Vhdl.Expressions.Expression assignmentExpression = 
					ConvertExpression(
						statements, 
						binExpr.RightChild, 
						expandExpression, 
						assignee.Type.Width, 
						false
					);

        SignalAssignmentStatement assignmentStatement =
          new SignalAssignmentStatement(
            assignee,
            assignmentExpression
          );

        statements.Add(assignmentStatement);
      }
    }

		/// <summary>
		/// Convert a Gezel unary expression into equivalent VHDL
		/// </summary>
		/// <param name="statements">The "current" statement collection</param>
		/// <param name="expr">Unary expression to convert</param>
		/// <param name="expandExpression"></param>
		/// <returns>Converted VHDL expression</returns>
    private Vhdl.Expressions.Expression convertUnaryExpression(
      StatementCollection statements, 
      Gezel.Model.UnaryExpression expr,
      bool expandExpression)
    {      
      Vhdl.Expressions.Expression childExpr =
        ConvertExpression(statements, expr.Child, expandExpression);

      switch (expr.ExpressionType)
      {
				// unary minus (-) operator
        case Weazel.Gezel.Model.ExpressionType.UnarySub:          
            return 
              new Vhdl.Expressions.UnaryExpression(
								statements,
                UnaryOperators.Sub, 
                childExpr, 
                expr.Signed ? 
                  (Vhdl.Types.Type) new Signed(expr.Width) :
                  (Vhdl.Types.Type) new Unsigned(expr.Width),
                new Vhdl.GezelType(expr.Signed, expr.Width),
                expr
              );           
				// unary not (~) operator (one's complement)
        case Weazel.Gezel.Model.ExpressionType.Not:                    
            return 
              new Vhdl.Expressions.UnaryExpression(
								statements,
                UnaryOperators.Not, 
                childExpr,
                expr.Signed ?
                  (Vhdl.Types.Type)new Signed(expr.Width) :
                  (Vhdl.Types.Type)new Unsigned(expr.Width),
                new Vhdl.GezelType(expr.Signed, expr.Width),
                expr
              );           
				// unknown
        default:          
            throw new NotSupportedException();          
      }
    }

		/// <summary>
		/// Convert a Gezel binary expression in to an equivalent VHDL expression 
		/// </summary>
		/// <param name="statements">The "current" statement collection</param>
		/// <param name="expr">Expression to convert</param>
		/// <param name="expandExpression"></param>
		/// <param name="targetWidth"></param>
		/// <param name="targetSign"></param>
		/// <returns></returns>
    private Vhdl.Expressions.Expression convertBinaryExpression(
      StatementCollection statements,
      Gezel.Model.BinaryExpression expr,
      bool expandExpression, 
			int targetWidth, 
			bool targetSign)
    {
      Vhdl.Expressions.BinaryExpression e = null;

      switch (expr.ExpressionType)
      {
				// Multiplication
        case Weazel.Gezel.Model.ExpressionType.Mul:
          {
            // target width of multiplication is different .. 
            int mulWidth = expr.LeftChild.Width + expr.RightChild.Width;

            Vhdl.Expressions.Expression leftExpr =
							ConvertExpression(statements, expr.LeftChild, expandExpression, mulWidth, expr.Signed);

            Vhdl.Expressions.Expression rightExpr =
							ConvertExpression(statements, expr.RightChild, expandExpression, mulWidth, expr.Signed);

            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              BinaryOperators.Mul,
              leftExpr,
              rightExpr,
              expr.Signed ?
								(Vhdl.Types.Type)new Signed(mulWidth) :
								(Vhdl.Types.Type)new Unsigned(mulWidth),
							new Vhdl.GezelType(expr.Signed, mulWidth)
            );

            break;
          }

        // aritmetic operators
        case Weazel.Gezel.Model.ExpressionType.Add:
        case Weazel.Gezel.Model.ExpressionType.Sub:        
        case Weazel.Gezel.Model.ExpressionType.Div:
        case Weazel.Gezel.Model.ExpressionType.Mod:
          {            
            Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression); 

            Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression); 

            int maxWidth = System.Math.Max(leftExpr.Type.Width, rightExpr.Type.Width);

            BinaryFunction op =
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Add ?
                  BinaryOperators.Add :
                expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Sub ?
                  BinaryOperators.Sub :
                expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Div ?
                  BinaryOperators.Div :
                  BinaryOperators.Mod;

            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              op, 
              leftExpr, 
              rightExpr,
              expr.Signed ?
                (Vhdl.Types.Type)new Signed(maxWidth) :
                (Vhdl.Types.Type)new Unsigned(maxWidth),
              new Vhdl.GezelType(expr.Signed, maxWidth),
              expr
            );

            break;
          }

        // logical operators
        case Weazel.Gezel.Model.ExpressionType.And:
        case Weazel.Gezel.Model.ExpressionType.Ior:
        case Weazel.Gezel.Model.ExpressionType.Xor:
          {            
            int maxWidth = System.Math.Max(expr.LeftChild.Width, expr.RightChild.Width);

            Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression, maxWidth, expr.Signed);

            Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression, maxWidth, expr.Signed);

            BinaryFunction op =
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.And ?
                BinaryOperators.And :
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Ior ?
                BinaryOperators.Or :
                BinaryOperators.Xor;

            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              op, 
              leftExpr, 
              rightExpr, 
              new Unsigned(expr.Width),
              new Vhdl.GezelType(expr.Signed, expr.Width),
              expr
            );

            break;
          }

        // shift/rotate functions
        case Weazel.Gezel.Model.ExpressionType.ShiftRight:
          {
						targetWidth = expr.Width;

						Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression, targetWidth, targetSign);

						Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression, targetWidth, targetSign);

						// note: using width of left child ..
						e = new Vhdl.Expressions.BinaryExpression(
							statements,
							BinaryOperators.ShiftRight,
							leftExpr,
							rightExpr,
							expr.Signed ?
								(Vhdl.Types.Type)new Signed(targetWidth) :
								(Vhdl.Types.Type)new Unsigned(targetWidth),
							new Vhdl.GezelType(expr.Signed, targetWidth),
							expr
						);

            break;
          }
        case Weazel.Gezel.Model.ExpressionType.ShiftLeft:
          {
						// calculating the width of the result of a shift left operation
						// is somewhat tricky. If the right hand side of the expression is
						// constant (number of bits to shift is constant) the width of the
						// result is simply left.width + right.value. If the right hand side
						// is not constant the width may be as wide as left.width + 1 << right.width.

						if (expr.RightChild.ExpressionType == Weazel.Gezel.Model.ExpressionType.Constant)
						{
							// if right side is constant width of result is left.width + right.value
							Weazel.Gezel.Model.LeafExpression constant =
								expr.RightChild as Weazel.Gezel.Model.LeafExpression;

							targetWidth = expr.LeftChild.Width + constant.IntegerValue.ToInt();
						}
						else
						{
							// otherwise, width of the result is left.width + 1 << right.width
							// Note: Calculating this width of the expression this way will cause
							// the synthesis tool to stall since the width may be extreme (65k!)
							// As a work around we use the target width ..							
						}

            Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression, targetWidth, targetSign);

            Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression, targetWidth, targetSign);

            // note: using width of left child ..
            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              BinaryOperators.ShiftLeft, 
              leftExpr, 
              rightExpr, 
              expr.Signed ?
								(Vhdl.Types.Type)new Signed(targetWidth) :
								(Vhdl.Types.Type)new Unsigned(targetWidth),
							new Vhdl.GezelType(expr.Signed, targetWidth),
              expr
            );

            break;
          }

        // .. concatenation ..
        case Weazel.Gezel.Model.ExpressionType.Concatenation:
          {						
						// NOTE: The children of the concatenation expression are swapped.
						//			 See note in parser. TODO

						Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression);

            Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression);

            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              BinaryOperators.Concatenation, 
              leftExpr, 
              rightExpr,
              expr.Signed ?
								(Vhdl.Types.Type)new Signed(expr.Width) :
								(Vhdl.Types.Type)new Unsigned(expr.Width),
							new Vhdl.GezelType(expr.Signed, expr.Width),
							expr					
						);

						break;
          }

        // comparsion operators
        case Weazel.Gezel.Model.ExpressionType.Eq:
        case Weazel.Gezel.Model.ExpressionType.Neq:
        case Weazel.Gezel.Model.ExpressionType.Lt:
        case Weazel.Gezel.Model.ExpressionType.Gt:
        case Weazel.Gezel.Model.ExpressionType.Lte:
        case Weazel.Gezel.Model.ExpressionType.Gte:
          {
            int maxWidth = System.Math.Max(expr.LeftChild.Width, expr.RightChild.Width);

            Vhdl.Expressions.Expression leftExpr = 
							ConvertExpression(statements, expr.LeftChild, expandExpression, maxWidth, false);

            Vhdl.Expressions.Expression rightExpr = 
							ConvertExpression(statements, expr.RightChild, expandExpression, maxWidth, false);

            IfStatement ifStatement = new IfStatement();

            BinaryFunction op =
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Eq ?
                BinaryOperators.Equals :
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Neq ?
                BinaryOperators.NotEquals :
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Lt ?
                BinaryOperators.LessThan :
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Gt ?
                BinaryOperators.GreaterThan :
              expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Lte ?
                BinaryOperators.LessThanOrEqual :
                BinaryOperators.GreaterThanOrEqual;

            e = new Vhdl.Expressions.BinaryExpression(
							statements,
              op, 
              leftExpr, 
              rightExpr,
              expr.Signed ?
                (Vhdl.Types.Type)new Signed(expr.Width) :
                (Vhdl.Types.Type)new Unsigned(expr.Width),
              new Vhdl.GezelType(expr.Signed, expr.Width),
              expr
            );

            ifStatement.Condition = e;

            Vhdl.Expressions.ConstantExpression ce = 
              new Weazel.Badger.Vhdl.Expressions.ConstantExpression(
								statements,
                "'1'",                
                new StdLogic(),
                null
              );

            Vhdl.Signal t = new Weazel.Badger.Vhdl.Signal(new StdLogic(), null);

            signalsCreated.Add(t);
            signalsWritten.Add(t);
            
            // this is not necessarily true ..
            signalsRead.Add(t);

            ifStatement.TrueStatements.Add(
              new SignalAssignmentStatement(
                t,
                ce
              )
            );

            ifStatement.FalseStatements.Add(
              new SignalAssignmentStatement(
                t,
                new Vhdl.Expressions.ConstantExpression(
									ifStatement.FalseStatements,
                  "'0'",
                  new StdLogic(),                 
                  null
                )
              )
            );

            statements.Add(ifStatement);

            return new Vhdl.Expressions.SignalReferenceExpression(statements, t);
          }

				// Unknown and unsupported expression type found
				default:
					{
						string message =
							String.Format("Unknown expression type, {0}, encountered.", expr.ExpressionType);

						throw new NotSupportedException(message);
					}
      }

      return e;
    }

    private string getIntegerInitializationString(string value)
    {
      switch (Weazel.Math.Integer.GetStringType(value))
      {
        case Weazel.Math.Integer.StringType.Binary:
          return "b\"" + value.Substring(2, value.Length - 2) + "\"";
        case Weazel.Math.Integer.StringType.Decimal:
          return value;
        case Weazel.Math.Integer.StringType.Hexadecimal:
          return "X\"" + value.Substring(2, value.Length - 2) + "\"";
        default:
          throw new ArgumentException("Unknown integer format");
      }
    }

    /// <summary>
    /// This method converts a gezel constant expression to VHDL code. 
    /// </summary>
    /// <param name="statements"></param>
    /// <param name="leaf">The gezel constant expression</param>
    /// <param name="expandExpression"></param>
    /// <param name="targetWidth"></param>
    /// <returns></returns>
    private Vhdl.Expressions.Expression ConvertConstant(
      StatementCollection statements,
      Gezel.Model.LeafExpression leaf,
      bool expandExpression,
      int targetWidth,
      bool targetSign
      )
    {
      // constants are a problem. The VHDL datatype for a constant is either 
      // integer if it is used in combination with another signal (or port) of
      // type std_logic_vector or character if used in combination with a signal
      // of type std_logic. Here the problem is tackled by allowing conversion 
      // between a subset of integers (0 to 9) and characters.

      // The width may differ from that of the gezel constant expression. 
      // If 'targetWidth' is different from -1 this value should be used instead.      
      int width = targetWidth != -1 ? targetWidth : leaf.Width;

      Vhdl.Types.Type type = width == 1 ?
          (Vhdl.Types.Type)new StdLogic()
        : (Vhdl.Types.Type)new StdLogicVector(width);
      
      Weazel.Math.Integer.StringType strType = Weazel.Math.Integer.GetStringType(leaf.Value);
      Weazel.Math.Integer integerValue = new Weazel.Math.Integer(leaf.Value);

      TypeSet possibleTypes = null;
			string value = integerValue.ToDecimalString();

      if (targetWidth == 1)
      {
        possibleTypes = new TypeSet(new StdLogic());       
      }
      else
      {
				if (strType == Weazel.Math.Integer.StringType.Hexadecimal)
				{
					possibleTypes = new TypeSet(new StdLogicVector(width));					
				}
				else if (strType == Weazel.Math.Integer.StringType.Binary)
				{
					possibleTypes = new TypeSet(new StdLogicVector(width));				
				}
				else if (strType == Weazel.Math.Integer.StringType.Decimal)				
				{
					possibleTypes = new TypeSet(new Integer(width));				
				}
				else
				{
					throw new NotSupportedException("Unsupported datatype");
				}        
			}

      Vhdl.ConstantDeclaration constant =
        new Weazel.Badger.Vhdl.ConstantDeclaration(
          possibleTypes,
          new Vhdl.GezelType(targetSign, width),
          integerValue
        );

      constants.Add(constant);

      Vhdl.Expressions.ConstantReferenceExpression refExpr =
        new Weazel.Badger.Vhdl.Expressions.ConstantReferenceExpression(
					statements,
          constant
        );

      return refExpr;
    }

    public Vhdl.Expressions.Expression ConvertExpression(
      StatementCollection statements, 
      Gezel.Model.Expression expr,
      bool expandExpression)
    {
      return ConvertExpression(statements, expr, expandExpression, -1, false);
    }

    public Vhdl.Expressions.Expression ConvertExpression(
      StatementCollection statements, 
      Gezel.Model.Expression expr,
      bool expandExpression,
      int targetWidth,
      bool targetSign)
    {
      Vhdl.Expressions.Expression e = null;

      if (expr is Gezel.Model.BinaryExpression)
      {
        Gezel.Model.BinaryExpression binaryExpr = (Gezel.Model.BinaryExpression)expr;

        if (binaryExpr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Assignment)
        {
          //throw new NotSupportedException();
          ConvertAssignmentExpression(binaryExpr, expandExpression);
        }
        else
        {
          e = convertBinaryExpression(statements, binaryExpr, expandExpression, targetWidth, targetSign);
        }        
      }

      // Ternary expressions can occure in the middle of an expression. Since there is no
      // ternary (assignment) expression in vhdl these must be converted to if-statements.
      // Here a new signal is created and the ternary expression is converted as if it was an
      // assignment expression with the newly created signal as the sink. This expression 
      // is inserted into the process and a reference to the signal is returned.
      else if (expr is Gezel.Model.TernaryExpression)
      {
        // create a new type
        Vhdl.Types.Type type = expr.Width == 1 ?
            (Vhdl.Types.Type) new StdLogic()
          : (Vhdl.Types.Type) new StdLogicVector(expr.Width);

        // create new signal
        Vhdl.Signal sig = new Vhdl.Signal(type, new Vhdl.GezelType(expr.Signed, expr.Width)); // must be updated by following call to convertTernaryExpression

        signalsCreated.Add(sig);
        signalsWritten.Add(sig);

        // add result of 'convertTernaryExpression' directly to the statement list of the
        // current process. The expression currently being converted will be inserted afterwards
        // once the recursive call to 'convertAssignmentExpression' returns - thus the
        // newly created signal will have the "correct" value in the current expression.
        statements.Add(convertTernaryExpression(statements, sig, (Gezel.Model.TernaryExpression)expr, expandExpression));
        
        e = new Vhdl.Expressions.SignalReferenceExpression(statements, sig);        
      }
      else if (expr is Gezel.Model.LeafExpression)
      {
        Gezel.Model.LeafExpression leaf = (Gezel.Model.LeafExpression)expr;

        if (expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Identifier)
        {
          // an identifier is either a signal or a port. Whatever it is it must
          // be added to the sensitivity list of the current process.

          // Lookup the identifier in the list of signals (and ports)
          // defined on the embedding entity.
          Vhdl.Signal signal = context.FindSignal(leaf.Value);
											
          // perhaps a local signal?
          if (signal == null)
            signal = context.FindSignal(signalFlowGraph.LocalName + "_" + leaf.Value);

          if (signal != null)
          {
            if (!signalsRead.Contains(signal))
              signalsRead.Add(signal);
          }
          // perhaps a constant?
          else if (context.Constants.ContainsKey(leaf.Value))
          {
            return ConvertConstant(statements, context.Constants[leaf.Value], expandExpression, targetWidth, targetSign);
          }
          // perhaps it is a look up table
          else if (context.LookUpTables.ContainsKey(leaf.Value))
          {
            Vhdl.ArrayTypeValue tab = context.LookUpTables[leaf.Value];            
            return new Vhdl.Expressions.ArrayVariableReferenceExpression(statements, tab);
          }
					// finally the identifier may reference a lookup table of length 1 converted
					// into a constant.
					else if (context.ConvertedLookUpTables.ContainsKey(leaf.Value))
					{
						Vhdl.Expressions.ConstantReferenceExpression reference =
							new Weazel.Badger.Vhdl.Expressions.ConstantReferenceExpression(
								statements,
								context.ConvertedLookUpTables[leaf.Value]
							);

						return reference;
					}
					else
					{
						string message =
							string.Format(
								"Failed to find target of identifier '{0}'",
								leaf.Value
							);

						throw new Vhdl.VhdlModelException(message);
					}

          e = new Vhdl.Expressions.SignalReferenceExpression(statements, signal);
        }
        else if (expr.ExpressionType == Weazel.Gezel.Model.ExpressionType.Constant)
        {
          e = ConvertConstant(statements, leaf, expandExpression, targetWidth, targetSign);
        }
        else
        {
          throw new Exception(String.Format("Unexpected expression type: {0}. Expected constant or identifier.", expr.ExpressionType));
        }

        // never expand
        return e;
      }
      else if (expr is Gezel.Model.CastExpression)
      {
				Gezel.Model.CastExpression castExpression =
					expr as Weazel.Gezel.Model.CastExpression;

				Vhdl.Expressions.Expression child =
					ConvertExpression(statements, castExpression.Child, true, targetWidth, targetSign);

				Vhdl.Expressions.SignalReferenceExpression signalRefernece = 
					insertSignalAssignmentExpression(statements, child, new Vhdl.GezelType(castExpression.Signed, castExpression.Width));

				e = signalRefernece;
      }
      else if (expr is Gezel.Model.RangeExpression)
      {
        Gezel.Model.RangeExpression range = (Gezel.Model.RangeExpression)expr;

        Vhdl.Expressions.Expression child = 
          ConvertExpression(
            statements, 
            range.Child, 
            expandExpression
          );

        if (range.To == range.From) // single bit selection
        {
          e = new Vhdl.Expressions.ArrayIndexExpression(
								statements,
                  child,
                  new Vhdl.Expressions.ConstantExpression(
										statements,
                    range.To.ToString(), 
                    new Integer(range.Width),
                    null,
                    expr
                  ),
                  new Vhdl.GezelType(expr.Signed, expr.Width),
                  expr
                );
        }
        else
        {
					uint max = System.Math.Max(range.To, range.From);
					uint min = System.Math.Min(range.To, range.From);

          e = new Vhdl.Expressions.ArrayIndexExpression(
									statements,
                  child,
                  new Vhdl.Expressions.ConstantExpression(
										statements,
										min.ToString(), 
										new Integer(range.Width), 
										null, expr
									),
                  new Vhdl.Expressions.ConstantExpression(
										statements,
										max.ToString(), 
										new Integer(range.Width), 
										null, 
										expr
									),
                  new Vhdl.GezelType(expr.Signed, expr.Width),
                  expr
                );


        }
      }
      else if (expr is Gezel.Model.IndexExpression)
      {
        Gezel.Model.IndexExpression indexExpression = (Gezel.Model.IndexExpression)expr;

        Vhdl.Expressions.Expression childExpr = ConvertExpression(statements, indexExpression.Identifier, expandExpression);
        Vhdl.Expressions.Expression indexExpr = ConvertExpression(statements, indexExpression.IndexExpr, expandExpression);

				if (childExpr is Vhdl.Expressions.ConstantReferenceExpression)
				{
					Vhdl.Expressions.ConstantReferenceExpression constant = childExpr as Vhdl.Expressions.ConstantReferenceExpression;

					System.Diagnostics.Debug.Assert(context.ConvertedLookUpTables.ContainsKey(constant.Constant.Name));
			//		System.Diagnostics.Debug.Assert(indexExpr is Vhdl.Expressions.ConstantExpression);

					return constant;
				}
				else if (childExpr is Vhdl.Expressions.ArrayVariableReferenceExpression)
				{
					return new Vhdl.Expressions.ArrayIndexExpression(
								statements,
								childExpr,
								indexExpr,
								new Weazel.Badger.Vhdl.GezelType(indexExpression.Signed, indexExpression.Width)
							);
				}
				else if (childExpr is Vhdl.Expressions.SignalReferenceExpression)
				{
					System.Diagnostics.Debug.Assert(childExpr.Type is StdLogicVector);

					e = new Vhdl.Expressions.ArrayIndexExpression(
								statements,
								childExpr,
								indexExpr,
								new Weazel.Badger.Vhdl.GezelType(indexExpression.Signed, indexExpression.Width)
							);
				}
				else
				{
					throw new NotSupportedException("problem figuring out lookup table");
				}
      }
      else if (expr is Gezel.Model.UnaryExpression)
      {
        return convertUnaryExpression(statements, (Gezel.Model.UnaryExpression)expr, expandExpression);
      }
      else if (expr is Gezel.Model.ListExpression)
      {
        throw new NotImplementedException();
      }
      else if (expr is Gezel.Model.DirectiveExpression)
      {
        Gezel.Model.DirectiveExpression directive = 
          (Gezel.Model.DirectiveExpression)expr;

        switch (expr.ExpressionType)
        {
          case Weazel.Gezel.Model.ExpressionType.DisplayDirective:
            insertDisplayStatement(statements, directive);
            break;

          default:
           // throw new NotImplementedException();
            break;
        }        
      }
      else
      {
        // unknown expression ..
        throw new NotSupportedException("Expression type not supported: " + expr.ExpressionType.ToString() + " (ConvertExpression)");
      }

      return expandExpression && e != null && !(e is Vhdl.Expressions.SignalReferenceExpression) ? insertSignalAssignmentExpression(statements, e) : e;
    }

    private void insertDisplayStatement(
      StatementCollection statements,
      Gezel.Model.DirectiveExpression directive
    )
    {    
      displayStatements.Add(new Comment("Converted display directive"));
      
			// default output is hexadecimal
      WriteProcedure.NumberFormat currentNumberStyleFormat =
       WriteProcedure.NumberFormat.Hexadecimal;

      foreach (Gezel.Model.Expression argument in directive.Arguments)
      {
        switch (argument.ExpressionType)
        {
          case Weazel.Gezel.Model.ExpressionType.Str:
            {
              Gezel.Model.LeafExpression leaf =
                (Gezel.Model.LeafExpression)argument;

              displayStatements.Add(
                new WriteProcedure("cur_line", WriteProcedure.InputFormat.String, leaf.Value)
              );

              break;
            }
          case Weazel.Gezel.Model.ExpressionType.HexDirective:
            {
              currentNumberStyleFormat =
                WriteProcedure.NumberFormat.Hexadecimal;
              break;
            }
          case Weazel.Gezel.Model.ExpressionType.DecDirective:
            {
              currentNumberStyleFormat =
                WriteProcedure.NumberFormat.Decimal;
              break;
            }
          case Weazel.Gezel.Model.ExpressionType.BinDirective:
            {
              currentNumberStyleFormat =
                WriteProcedure.NumberFormat.Binary;
              break;
            }
          case Weazel.Gezel.Model.ExpressionType.CycleDirective:
            {
              displayStatements.Add(
                new WriteProcedure("cur_line", WriteProcedure.InputFormat.Identifier, "cycles")
              );
              break;
            }
          default:
            {
              // this is either a signal, register or an expression
              // lets find out which ..

              Vhdl.Expressions.Expression expression =
                ConvertExpression(statements, argument, true);

              if (expression is Vhdl.Expressions.SignalReferenceExpression)
              {
                Vhdl.Expressions.SignalReferenceExpression signalReference =
                 (Vhdl.Expressions.SignalReferenceExpression)expression;

                if (signalReference.Signal is RegisterReadSignal)
                {
                  RegisterSignal register =
                    (RegisterSignal)signalReference.Signal;

                  displayStatements.Add(
                    new WriteProcedure(
                      "cur_line",
                      register.Register.ReadSignal,
                      currentNumberStyleFormat
                    )
                  );

                  displayStatements.Add(
                    new WriteProcedure("cur_line", WriteProcedure.InputFormat.String, "/"));

                  displayStatements.Add(
                    new WriteProcedure(
                      "cur_line",
                      register.Register.WriteSignal,
                      currentNumberStyleFormat
                    )
                  );
                }
                else
                {
                  displayStatements.Add(
                    new WriteProcedure(
                      "cur_line",
                      signalReference.Signal,
                      currentNumberStyleFormat
                    )
                  );
                }
              }
              else
              {
                throw new NotImplementedException();
              }

              break;
            }
        }        
      }

      displayStatements.Add(new WriteLineProcedure("cur_line", "display_file"));
    }

    // A Gezel expression cannot always be converted to an 
    // equivalent VHDL expression. For instance VHDL does not support
    // the ternary assignment operator which must be implemented as a
    // seperate if-then-else statement.
    public void Convert(
      StatementCollection statements, 
      Gezel.Model.Expression expr,
      bool expandExpression)
    {

      if (statements == null)
        throw new NullReferenceException();
      /*  statements = new StatementCollection();
      */

      ConvertExpression(statements, expr, expandExpression);
    }

    public ExpressionConverter(ConversionEntity context, Gezel.Model.SignalFlowGraph sfg)
    {
      this.context = context;
      this.signalFlowGraph = sfg;
    }
  }
}
